package simuladores;

import java.math.BigDecimal;
import java.math.RoundingMode;

import util.Operacao;
import util.Real;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

import custos.CalculadorDeCusto;

/**
 * Simula uma operacao de venda de um ativo ao preco de StopLoss, recompra ao
 * precoMinimo e revenda a um preco tal que haja lucro.
 * 
 * @author pisaruk
 * 
 */
public class SimulacaoRebound {
	private CalculadorDeCusto calculadorDeCusto;

	private BigDecimal quantidadeComprada;

	private Real investimentoInicial;

	private Real precoDeCompra;

	/**
	 * Retorna a quantidade possivel de ser comprada de um certo ativo com base
	 * no montante de dinheiro informado e no preco do ativo.
	 * 
	 * @param precoDoAtivo
	 * @param dinheiroDisponivel
	 * @return
	 */
	private BigDecimal getQuantidadePorMontante(Real precoDoAtivo,
			Real dinheiroDisponivel) {
		BigDecimal quantidade = dinheiroDisponivel.divide(precoDoAtivo)
				.getValor().setScale(0, RoundingMode.DOWN);
		if (quantidade.compareTo(new BigDecimal(0)) <= 0)
			return new BigDecimal(0);
		while (getInvestimento(quantidade, precoDoAtivo).compareTo(
				dinheiroDisponivel) > 0) {
			quantidade = quantidade.subtract(new BigDecimal(1)).setScale(0,
					RoundingMode.DOWN);
		}
		return quantidade;
	}

	private Real getInvestimento(BigDecimal quantidade, Real preco) {
		return calculadorDeCusto.calcular(quantidade, preco,
				Operacao.operacaoFinal, null).add(preco.multiply(quantidade));
	}

	public String toString() {
		XStream xstream = new XStream(new DomDriver());
		xstream.alias(this.getClass().getName().substring(
				this.getClass().getPackage().getName().length() + 1), this
				.getClass());
		return xstream.toXML(this);
	}

	/**
	 * Retorna o preco minimo de venda do ativo para se obtenha lucro.
	 * 
	 * @return
	 */
	public Real getPrecoMinimoDeVenda(Real stopLoss, Real precoMinimo) {
		System.out.println("investimentoInicial=" + investimentoInicial);
		Real montanteDisponivelAposVenda = stopLoss
				.multiply(quantidadeComprada).subtract(
						calculadorDeCusto.calcular(quantidadeComprada,
								stopLoss, Operacao.operacaoFinal, null));
		System.out.println("montante disponivel apos venda a " + stopLoss
				+ " = " + montanteDisponivelAposVenda);
		BigDecimal quantidade = getQuantidadePorMontante(precoMinimo,
				montanteDisponivelAposVenda);
		System.out.println("quantida comprada a " + precoMinimo + " = "
				+ quantidade);
		System.out.println("investimento fundo="
				+ getInvestimento(quantidade, precoMinimo));
		Real restante = montanteDisponivelAposVenda.subtract(getInvestimento(
				quantidade, precoMinimo));
		System.out.println("restante=" + restante);
		Real retornoFinal;
		Real incremento = new Real("0.01");
		Real precoAtual = precoMinimo;
		do {
			retornoFinal = precoAtual.multiply(quantidade).subtract(
					calculadorDeCusto.calcular(quantidade, precoAtual,
							Operacao.operacaoFinal, null)).add(restante);
			precoAtual = precoAtual.add(incremento);
		} while (retornoFinal.compareTo(investimentoInicial) < 0);
		System.out.println("retornoFinal = " + retornoFinal);
		System.out.println("Preco Minimo de Venda = " + precoAtual);
		System.out.println("Lucro = "
				+ retornoFinal.subtract(investimentoInicial));
		return precoAtual;
	}

	/**
	 * 
	 * @param quantidadeComprada
	 * @param precoDeCompra
	 * @param precoMinimo
	 * @param calculadorDeCusto
	 */
	public SimulacaoRebound(BigDecimal quantidadeComprada, Real precoDeCompra,
			CalculadorDeCusto calculadorDeCusto) {
		this.calculadorDeCusto = calculadorDeCusto;
		this.quantidadeComprada = quantidadeComprada;
		this.precoDeCompra = precoDeCompra;
		investimentoInicial = getInvestimento(quantidadeComprada, precoDeCompra);
	}

	public Real getMinStopLoss(Real precoMinimo) {
		Real stopLoss = precoDeCompra;
		Real minStopLoss = null;
		while (stopLoss.compareTo(precoMinimo) >= 0) {
			Real precoVenda = getPrecoMinimoDeVenda(stopLoss, precoMinimo);
			if (precoVenda.compareTo(precoDeCompra) > 0)
				break;
			minStopLoss = stopLoss;
			System.out.println("stopLoss=" + stopLoss + "\n");
			stopLoss = stopLoss.subtract(new BigDecimal(0.01).setScale(2,
					RoundingMode.DOWN));
		}
		return minStopLoss;
	}

	public Real getCorrecaoTecnica(Real precoAtual, BigDecimal quantidade) {
		Real montanteAtualAposVenda = precoAtual.multiply(quantidade).subtract(
				calculadorDeCusto.calcular(quantidade, precoAtual,
						Operacao.operacaoFinal, null));
		Real novoValor;
		Real correcaoTecnica;
		Real preco = precoAtual;
		System.out.println("preco;valor;correcaoTecnica;resto;novoValor");
		do {
			preco = preco.subtract(new BigDecimal("0.01"));
			// venda
			Real valor = preco.multiply(quantidade).subtract(
					calculadorDeCusto.calcular(quantidade, preco,
							Operacao.operacaoFinal, null));
			correcaoTecnica = preco.subtract(preco.multiply(new BigDecimal(
					"0.02")));
			BigDecimal novaQuantidade = getQuantidadePorMontante(
					correcaoTecnica, valor);
			Real resto = valor.subtract(
					correcaoTecnica.multiply(novaQuantidade)).subtract(
					calculadorDeCusto.calcular(novaQuantidade, correcaoTecnica,
							Operacao.operacaoFinal, null));
			novoValor = precoAtual.multiply(novaQuantidade).subtract(
					calculadorDeCusto.calcular(novaQuantidade, precoAtual,
							Operacao.operacaoFinal, null)).add(resto);
			System.out.println(preco + ";" + valor + ";" + correcaoTecnica
					+ ";" + resto + ";" + novoValor);
		} while (novoValor.compareTo(montanteAtualAposVenda) < 0);
		return correcaoTecnica;
	}
}